import ImCommon
import UIKit

// 带内部边距的标签
class MyLabel: UILabel {
    private var mContentInset: UIEdgeInsets? // 内部边距
    
    /**
     * 获取/设置内部边距。
     */
    public var margin: CGFloat {
        get {
            if mContentInset == nil {
                return 0
            }
            return mContentInset!.left
        }
        set {
            mContentInset = UIEdgeInsets(top: newValue, left: newValue, bottom: newValue, right: newValue)
        }
    }
    
    /**
     * 画文字。
     * @param rect  矩形区域
     */
    override func drawText(in rect: CGRect) {
        if mContentInset != nil {
            super.drawText(in: rect.inset(by: mContentInset!))
        } else {
            super.drawText(in: rect)
        }
    }
    
    /**
     * 获取文字矩形区域。
     * @param bounds        原来的矩形区域
     * @param numberOfLines 行号
     */
    internal override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        if mContentInset != nil {
            var rect = super.textRect(forBounds: bounds.inset(by: mContentInset!), limitedToNumberOfLines: numberOfLines)
            rect.origin.x -= mContentInset!.left
            rect.origin.y -= mContentInset!.top
            rect.size.width += mContentInset!.left + mContentInset!.right
            rect.size.height += mContentInset!.top + mContentInset!.bottom
            return rect
        }
        return bounds
    }
}

// 聊天表项视图
class ChatCell: UITableViewCell {
    public let mImageViewUserIcon: UIImageView  // 头像视图
    public let mLabelContent: MyLabel           // 内容标签
    public let mLabelTime: UILabel              // 时间标签
    
    /**
     * 构造方法。
     * @param item 表项
     */
    public init(_ item: ChatListItem) {
        // 初始化成员
        mImageViewUserIcon = UIImageView()
        mImageViewUserIcon.image = item.icon

        mLabelContent = MyLabel()
        mLabelContent.margin = ChatWindow.sPadding
        mLabelContent.numberOfLines = 0
        mLabelContent.text = item.content
        mLabelContent.sizeToFit()
        if item.isLeft {
            mLabelContent.layer.borderColor = Const.CHAT_WINDOW_LABEL_CONTENT_BORDER_COLOR_LEFT
            mLabelContent.layer.borderWidth = 1
        } else {
            mLabelContent.layer.backgroundColor = Const.CHAT_WINDOW_LABEL_CONTENT_BACKGROUND_COLOR_RIGHT
            mLabelContent.textColor = Const.CHAT_WINDOW_LABEL_CONTENT_TEXT_COLOR_RIGHT
        }
        mLabelContent.layer.cornerRadius = Const.CHAT_WINDOW_LABEL_CONTENT_CORNER_RADIUS

        mLabelTime = UILabel()
        mLabelTime.text = item.time
        mLabelTime.textColor = Const.CHAT_WINDOW_LABEL_TIME_TEXT_COLOR
        
        super.init(style: .default, reuseIdentifier: "ID_CHAT_CELL")
        
        super.addSubview(mImageViewUserIcon)
        super.addSubview(mLabelContent)
        super.addSubview(mLabelTime)

        // 计算控件尺寸
        let screenWidth = UIScreen.main.bounds.width
        let iconSize = screenWidth * Const.SCALE_CHAT_WINDOW_ICON_SIZE

        // 禁用 translatesAutoresizingMaskIntoConstraints，否则控件约束将无效
        mImageViewUserIcon.translatesAutoresizingMaskIntoConstraints = false
        mLabelTime.translatesAutoresizingMaskIntoConstraints = false
        
        // 添加控件约束
        /* 时间标签 */
        let lcLabelTimeCenterX = NSLayoutConstraint(item: mLabelTime, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0)
        super.addConstraint(lcLabelTimeCenterX)
        
        let lcLabelTimeTop = NSLayoutConstraint(item: mLabelTime, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0)
        super.addConstraint(lcLabelTimeTop)
        
        /* 头像视图 */
        let lcImageViewIconWidth = NSLayoutConstraint(item: mImageViewUserIcon, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: iconSize)
        super.addConstraint(lcImageViewIconWidth)

        let lcImageViewIconHeight = NSLayoutConstraint(item: mImageViewUserIcon, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: iconSize)
        super.addConstraint(lcImageViewIconHeight)
        
        if item.isLeft {
            let lcImageViewIconLeft = NSLayoutConstraint(item: mImageViewUserIcon, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: ChatWindow.sPadding)
            super.addConstraint(lcImageViewIconLeft)
        } else {
            let lcImageViewIconRight = NSLayoutConstraint(item: mImageViewUserIcon, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: -ChatWindow.sPadding)
            super.addConstraint(lcImageViewIconRight)
        }

        let lcImageViewIconTop = NSLayoutConstraint(item: mImageViewUserIcon, attribute: .top, relatedBy: .equal, toItem: mLabelTime, attribute: .bottom, multiplier: 1.0, constant: ChatWindow.sPadding)
        super.addConstraint(lcImageViewIconTop)

        /* 内容标签 */
        let maxLabelWidth = screenWidth - iconSize - ChatWindow.sPadding * 3
        let size = NSString(string: mLabelContent.text!).boundingRect(with: CGSize(width: maxLabelWidth, height: 0), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: mLabelContent.font!], context: nil)
        var labelContentX: CGFloat = 0
        if item.isLeft {
            labelContentX = iconSize + ChatWindow.sPadding * 2
        } else {
            if mLabelContent.frame.width > maxLabelWidth {
                labelContentX = screenWidth - iconSize - size.width - ChatWindow.sPadding * 2
            } else {
                labelContentX = screenWidth - iconSize - mLabelContent.frame.width - ChatWindow.sPadding * 2
            }
        }

        let labelContentY = mLabelTime.frame.height + ChatWindow.sPadding * 3
        if mLabelContent.frame.width > maxLabelWidth {
            mLabelContent.frame = CGRect(x: labelContentX, y: labelContentY, width: size.width, height: size.height)
            mLabelContent.sizeToFit()
        } else {
            mLabelContent.frame.origin = CGPoint(x: labelContentX, y: labelContentY)
        }
    }

    /**
     * 必要的构造方法。
     * @param coder 解码器
     */
    public required init?(coder: NSCoder) {
        mImageViewUserIcon = UIImageView()
        mLabelContent = MyLabel()
        mLabelTime = UILabel()
        super.init(coder: coder)
    }
}

// 聊天表项
class ChatListItem {
    private let mContent: String    // 聊天内容
    private let mIcon: UIImage      // 头像
    private let mIsLeft: Bool       // 左右标志，true 为左，false 为右
    private let mTime: String       // 发送时间

    /**
     * 构造方法。
     * @param content   聊天内容
     * @param iconIndex 头像索引
     * @param isLeft    左右标志，true 为左，false 为右
     * @param time      发送时间
     */
    public init(_ content: String, iconIndex: Int, isLeft: Bool, time: String) {
        mContent = content
        let fileName = String(format: Const.ICON_FILE_NAME, iconIndex)
        mIcon = UIImage(named: fileName)!
        mIsLeft = isLeft
        mTime = time
    }

    /**
     * 获取聊天内容。
     */
    public var content: String {
        get {
            return mContent
        }
    }

    /**
     * 获取头像。
     */
    public var icon: UIImage {
        get {
            return mIcon
        }
    }

    /**
     * 获取左右标志。
     */
    public var isLeft: Bool {
        get {
            return mIsLeft
        }
    }
    
    /**
     * 获取时间。
     */
    public var time: String {
        get {
            return mTime
        }
    }
}

// 聊天窗口
class ChatWindow: UIViewController, UITableViewDataSource, UITableViewDelegate {
    private var mCellList: Array<ChatCell>                      // 所有表项视图列表
    private var mChatList: Array<ChatListItem>                  // 聊天表项列表
    @IBOutlet private weak var mEditInput: UITextField!         // 聊天内容编辑框
    @IBOutlet private weak var mTableViewHistory: UITableView!  // 聊天历史控件
    public static var sPadding: CGFloat = 0                     // 控件间距

    /**
     * 必要的构造方法。
     * @param coder 解码器
     */
    public required init?(coder: NSCoder) {
        mCellList = Array<ChatCell>()
        mChatList = Array<ChatListItem>()
        super.init(coder: coder)
        ChatWindow.sPadding = UIScreen.main.bounds.width * Const.SCALE_CHAT_WINDOW_PADDING
    }

    /**
     * 添加表项。
     * @param chatInfo  聊天信息
     */
    public func addItem(_ chatInfo: ChatInfo) {
        // 显示聊天内容
        let isLeft = Client.username != chatInfo.mSrcUsername
        let item = ChatListItem(chatInfo.mContent, iconIndex: chatInfo.mSrcIconIndex, isLeft: isLeft, time: chatInfo.mTime)
        mChatList.append(item)
        if mTableViewHistory == nil {
            return
        }
        mTableViewHistory!.reloadData()

        // 滚动到底部
        DispatchQueue.main.async {
            [self] in
            let contentOffsetHeight = mTableViewHistory!.contentSize.height - mTableViewHistory!.frame.size.height
            if contentOffsetHeight > 0 {
                let bottomOffset = CGPoint(x: 0, y: contentOffsetHeight)
                mTableViewHistory?.setContentOffset(bottomOffset, animated: false)
            }
        }
    }

    /**
     * 清除所有聊天记录。
     */
    public func clear() {
        mChatList.removeAll()
    }

    /**
     * 获取列表视图的所有 section 的个数。
     * @param tableView 列表视图
     * @return 所有 section 的个数
     */
    internal func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    /**
     * 获取一个 section 的所有 cell 数。
     * @param tableView 列表视图
     * @param section   第几个 section
     * @return 一个 section 的所有 cell 数
     */
    internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return mChatList.count
    }

    /**
     * 获取表项视图。
     * @param tableView 列表视图
     * @param indexPath 行号
     * @return 表项视图
     */
    internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if mCellList.count >= indexPath.row + 1 {
            return mCellList[indexPath.row]
        }

        // 获取表项
        let cell = ChatCell(mChatList[indexPath.row])
        
        // 加入列表
        mCellList.append(cell)

        return cell
    }
    
    /**
     * 获取表项视图的高度。
     * @param tableView 列表视图
     * @param indexPath 行号
     * @return 表项视图的高度
     */
    internal func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if mCellList.count < indexPath.row + 1 {
            return 0
        }

        let cell = mCellList[indexPath.row]
        return cell.mLabelTime.frame.height + max(cell.mImageViewUserIcon.frame.height, cell.mLabelContent.frame.height) + ChatWindow.sPadding * 3
    }

    /**
     * 设置编辑框最大输入长度。
     * @param textField 编辑框
     * @param range     ?
     * @param string    ?
     * @return 不超过最大长度则返回 true，否则返回 false
     */
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField == mEditInput {
            let len = textField.text!.count + string.count - range.length
            return len <= Const.MAX_CONTENT_LEN
        }
        return false
    }

    /**
     * "发送"按钮的单击响应方法。
     */
    @IBAction private func onButtonSendClicked() {
        // 焦点切换到编辑框
        mEditInput.becomeFirstResponder()

        // 获取用户输入的聊天内容
        let content =  mEditInput.text!

        // 获取当前时间
        let formatter = DateFormatter()
        formatter.dateFormat = Const.DATE_TIME_FORMAT
        let time = formatter.string(from: Date())

        // 生成聊天信息
        let chatInfo = ChatInfo(content, dstUsername: title!, srcIconIndex: Client.iconIndex, srcUsername: Client.username, time: time)

        // 显示聊天内容
        self.addItem(chatInfo)

        // 发送聊天信息
        NotificationCenter.default.post(name: Const.UM_CHAT_C2S, object: chatInfo)
        
        // 清空编辑框
        mEditInput.text = ""
    }
    
    /**
     * 视图加载事件的响应方法。
     */
    internal override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置代理
        mTableViewHistory.dataSource = self
        mTableViewHistory.delegate = self
        
        // 禁用 translatesAutoresizingMaskIntoConstraints，否则控件约束将无效
        mEditInput.translatesAutoresizingMaskIntoConstraints = false
        mTableViewHistory.translatesAutoresizingMaskIntoConstraints = false
        let buttonSend = view.viewWithTag(Const.TAG_BUTTON_SEND)!
        buttonSend.translatesAutoresizingMaskIntoConstraints = false

        // 计算控件尺寸
        let screenWidth = UIScreen.main.bounds.width
        let screenHeight = UIScreen.main.bounds.height
        let padding = screenWidth * Const.SCALE_CHAT_WINDOW_PADDING
        let screenY = (navigationController?.navigationBar.frame.height)! + UIApplication.shared.windows.first!.windowScene!.statusBarManager!.statusBarFrame.height
        let eiWidth = screenWidth - padding * 3 - buttonSend.frame.width
        
        // 添加控件约束
        /* 聊天历史控件 */
        let lcTableViewHistoryLeft = NSLayoutConstraint(item: mTableViewHistory!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0)
        view.addConstraint(lcTableViewHistoryLeft)
        
        let lcTableViewHistoryRight = NSLayoutConstraint(item: mTableViewHistory!, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 0)
        view.addConstraint(lcTableViewHistoryRight)

        let lcTableViewHistoryTop = NSLayoutConstraint(item: mTableViewHistory!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: screenY + padding)
        view.addConstraint(lcTableViewHistoryTop)
        
        let tvHeight = screenHeight - screenY - mEditInput.frame.height - padding * 3
        let lcTableViewHistoryHeight = NSLayoutConstraint(item: mTableViewHistory!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: tvHeight)
        mTableViewHistory.addConstraint(lcTableViewHistoryHeight)
        
        /* 聊天内容编辑框 */
        let lcEditInputLeft = NSLayoutConstraint(item: mEditInput!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: padding)
        view.addConstraint(lcEditInputLeft)

        let lcEditInputWidth = NSLayoutConstraint(item: mEditInput!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: eiWidth)
        mEditInput.addConstraint(lcEditInputWidth)

        let lcEditInputBottom = NSLayoutConstraint(item: mEditInput!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: -padding)
        view.addConstraint(lcEditInputBottom)

        /* "发送"按钮 */
        let lcButtonSendRight = NSLayoutConstraint(item: buttonSend, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: -padding)
        view.addConstraint(lcButtonSendRight)

        let lcButtonSendCenterY = NSLayoutConstraint(item: buttonSend, attribute: .centerY, relatedBy: .equal, toItem: mEditInput, attribute: .centerY, multiplier: 1.0, constant: 0)
        view.addConstraint(lcButtonSendCenterY)
    }
}
